Framework.Objects = {}

STACK_LIFO = 1
STACK_FILO = 1
STACK_FIFO = 2
STACK_LILO = 2
STACK_OVERFLOW = 4

local StackObj = {}

function Framework.Objects.Stack( FG_Flags, OP_NU_Size )
	return setmetatable( { Size = OP_NU_Size or 65536, Flags = FG_Flags, Stack = {}, Counter = 0 }, StackObj )
end

function StackObj:__index( b )
	if( type( b ) == "number" ) then
		if( math.abs( b ) <= self.Counter ) then
			if( self.Flags & 1 > 0 && b < 0 ) then
				return self.Stack[self.Counter+b+1]
			elseif( self.Flags & 2 > 0 && b < 0 ) then
				return self.Stack[math.abs(b)]
			else
				return self.Stack[b]
			end
		else
			if( math.abs( b ) > self.Size ) then				
				Framework.IO.Warning( "Attempted to index out-of-bounds value in Stack!" )
			end
			return nil
		end
	else
		return StackObj[b]
	end
end

function StackObj:Push( a )
	if( self.Counter >= self.Size ) then
		if( self.Flags & STACK_OVERFLOW > 0 ) then
			if( self.Flags & 1 > 0 ) then
				table.remove( self.Stack, 1 )
			else
				self.Stack[self.Size] = nil
			end
			self.Counter = self.Counter - 1
		else
			Framework.IO.Warning( "Stack overflow" )
			return
		end
	end
	self.Counter = self.Counter + 1
	
	if( self.Flags & 1 > 0 ) then
		self.Stack[self.Counter] = a
	else
		table.insert( self.Stack, 1, a )
	end
end

function StackObj:Pop()
	if( self.Counter == 0 ) then
		Framework.IO.Warning( "Attempted to pop value from empty Stack!" )
		return nil
	end
	
	local Ret
	if( self.Flags & 1 > 0 ) then
		Ret = self.Stack[self.Counter]
	else
		Ret = self.Stack[1]
		table.remove( self.Stack, 1 )
	end
	self.Counter = self.Counter - 1
	return Ret
end

function StackObj:Sum( Func )
	local Func = Func or function(a,b) return ( a != nil && a + b ) || b end
	local Sum = 0
	for _, Value in pairs( self.Stack ) do
		Sum = Func( Sum, Value )
	end
	return Sum
end

function StackObj:Average( OP_FU_Add, OP_FU_Divide )
	local Sum = 0
	local Counter = 0
	for k, v in pairs( self.Stack ) do
		Counter = Counter + 1
		Sum = OP_FU_Add && OP_FU_Add( Sum, v ) || Sum + v
	end
	return OP_FU_Divide && OP_FU_Divide( Sum, Counter ) || Sum / Counter
end

function StackObj:High( OP_FU_Comparison )
	local High = -math.huge
	for k, v in pairs( self.Stack ) do
		if( OP_FU_Comparison && OP_FU_Comparison( High, v ) || v > High ) then
			High = v
		end
	end
	return High
end

function StackObj:Low( OP_FU_Comparison )
	local Low = math.huge
	for k, v in pairs( self.Stack ) do
		if( OP_FU_Comparison && OP_FU_Comparison( Low, v ) || v < Low ) then
			Low = v
		end
	end
	return Low
end

function StackObj:List()
	return self.Stack
end

function StackObj:GetSize()
	return self.Size
end

function StackObj:__add( b )
	return self[b]
end

function StackObj:__sub( b )
	return self[-b]
end

/**************************************************************************************************************
*** Stack object example for solving the second problem on ProjectEuler.net using a First-In-Last-Out Stack ***
***************************************************************************************************************

MyStack = Framework.Objects.Stack( STACK_FILO )
MyStack:Push( 1 )
MyStack:Push( 2 )
while( MyStack[-1] < 4000000 ) do
	MyStack:Push( MyStack[-1] + MyStack[-2] )
end

local Sum = MyStack:Sum( function(a) 
	-- Only return the value if it's even
	if( a % 2 != 0 ) then 
		return {0} 
	end 
	return {a} 
end )

print( "Sum of all even numbers in the fibonacci sequence (under 4million): " .. Sum[1] )
*/


